#include "Task_Process_Pool.hpp"

using namespace std;

const int num = 5;
static int CNT = 1;
struct channel
{
    int _ctrlfd;
    pid_t _workerId;
    std::string _name;

public:
    channel(int fd, pid_t id)
        : _ctrlfd(fd), _workerId(id)
    {
        this->_name = "channel-" + to_string(CNT++);
    }
};

void Work()
{
    while (true)
    {
        int code;
        int n = read(0, &code, sizeof(code));
        if (n == 0)
        {

            break;
        }
        else if (n > 0)
        {
            if (!init.CheckSafe(code))
                continue;
            else
            {
                init.RunTask(code);
            }
        }
        else
        {
            cerr << "read error" << endl;
        }
    }
    cout << "my task is ended,pid is " << getpid() << endl;
}

void printDebug(const vector<channel> &c)
{
    for (const auto &channel : c)
    {
        cout << channel._name << "," << channel._ctrlfd << ',' << channel._workerId << endl;
    }
}

void PrintCloseFD(const vector<int> &FDs)
{
    for (auto fd : FDs)
    {
        cout << "process:" << getpid() << "close the FD:" << fd << endl;
    }
}

void CreatChannels(vector<channel> &channels)
{

    //以下代码是由bug的，会导致文件描述符的错乱，导致子进程多余的文件描述符指向
    //导致释放的时候出现问
    // for (int i = 0; i < num; i++)
    // {
    //     int pipfd[2];
    //     int n = pipe(pipfd);
    //     assert(n == 0);
    //     pid_t id = fork();
    //     assert(id != -1);

    //     if (id == 0)
    //     {
    //         // child
    //         close(pipfd[1]);

    //         //这里重定向，能够很方便直接从标准输入里面读取
    //         //可以直接读取0号

    //         dup2(pipfd[0], 0);
    //         Work();
    //         close(pipfd[0]);
    //         exit(0);
    //     }

    //     // father
    //     close(pipfd[0]);
    //     channels.push_back(channel(pipfd[1], id));
    // }

    //为了解决上述问题，写如下代码
    vector<int> temp;
    for (int i = 0; i < num; i++)
    {
        int pipfd[2];
        int n = pipe(pipfd);
        assert(n == 0);
        pid_t id = fork();
        assert(id != -1);

        if (id == 0)
        {
            // child
            if (!temp.empty())
            {
                for (auto fd : temp)
                {
                    close(fd);
                }
                PrintCloseFD(temp);
            }

            close(pipfd[1]);

            //这里重定向，能够很方便直接从标准输入里面读取
            //可以直接读取0号

            dup2(pipfd[0], 0);
            Work();
            close(pipfd[0]);
            exit(0);
        }

        // father
        close(pipfd[0]);
        channels.push_back(channel(pipfd[1], id));
        temp.push_back(pipfd[1]);
    }
}

void SendCommand(const std::vector<channel> &channels, bool Isloop = true, int TaskNum = -1)
{
    int pos = 0;
    while (true)
    {
        int command = init.SelectTask();
        const auto &c = channels[pos++];
        pos %= channels.size();
        // 3.发送任务
        sleep(1);
        cout << "send Command task " << command << " to" << c._name << " Worker is " << c._workerId << endl;
        write(c._ctrlfd, &command, sizeof(command));

        if (!Isloop)
        {
            TaskNum--;
            if (TaskNum <= 0)
            {
                cout << "Send done\n";
                break;
            }
        }
    }
}

void ReleaseChannels(const vector<channel> &channels)
{
    // version2也可以顺着解决这个问题
    for (int i = num - 1; i >= 0; i--)
    {
        close(channels[i]._ctrlfd);
        waitpid(channels[i]._workerId, nullptr, 0);
    }

    //解决由于子进程反复创建，和文件描述符创建的问题,会导致后面的子进程会有写端指向之前的写端
    // version1 ,
    // for (const auto &c : channels)
    // {
    //     close(c._ctrlfd);
    // }
    // for (const auto &channel : channels)
    // {
    //     pid_t rid = waitpid(channel._workerId, nullptr, 0);
    //     if (rid = channel._workerId)
    //     {
    //         cout << "wait child success : " << channel._workerId << endl;
    //     }
    // }
}
int main()
{
    vector<channel> channels;
    CreatChannels(channels);

    //父进程处理任务
    // 1.选择任务
    // 2.选择信道
    SendCommand(channels, false, 2);
    ReleaseChannels(channels);
    return 0;
}